-
Notifications
You must be signed in to change notification settings - Fork 5.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[WIP] Support role management #7346
Conversation
Can you add documentation for the two commands? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Squash "Add product tests for CREATE/DROP role" (1341f67a6bffa16236bb7dcf4a677ddb5632d925)
with "Implement CREATE/DROP role for Hive connector" (66eb6fb8018d1c186f803aa4b8bfb84690262414). They don't make sense separately.
assertStatement("CREATE ROLE role3 IN catalog1", new CreateRole("role3", Optional.empty(), Optional.of("catalog1"))); | ||
assertStatement("CREATE ROLE \"role\" WITH ADMIN \"admin\" IN \"catalog\"", new CreateRole("role", Optional.of("admin"), Optional.of("catalog"))); | ||
assertStatement("CREATE ROLE \"ro le\" WITH ADMIN \"ad min\" IN \"ca talog\"", new CreateRole("ro le", Optional.of("ad min"), Optional.of("ca talog"))); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please add tests for non-letter chars in role, admin and catalog (depending on if they're accepted or not, expect failure here or proper CreateRole
object). E.g. role[]
or (admin)
etc.
assertStatement("DROP ROLE role1 IN catalog", new DropRole("role1", Optional.of("catalog"))); | ||
assertStatement("DROP ROLE \"role\" IN \"catalog\"", new DropRole("role", Optional.of("catalog"))); | ||
assertStatement("DROP ROLE \"ro le\" IN \"ca talog\"", new DropRole("ro le", Optional.of("ca talog"))); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ditto non-letter chars.
@@ -253,6 +253,16 @@ | |||
Optional<ResolvedIndex> resolveIndex(Session session, TableHandle tableHandle, Set<ColumnHandle> indexableColumns, Set<ColumnHandle> outputColumns, TupleDomain<ColumnHandle> tupleDomain); | |||
|
|||
/** | |||
* Create role |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Explain any constraints on grantor maybe? Are there any? What if grantor
is empty? This comment is useless currently.
{ | ||
Optional<CatalogMetadata> catalogMetadata = getOptionalCatalogMetadata(session, catalog); | ||
if (!catalogMetadata.isPresent()) { | ||
return ImmutableSet.of(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not throw here? If catalog does not exist but was used, it's an exceptional situation, isn't it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've taken the approach from listSchemaNames
method
throw new PrestoException(NOT_SUPPORTED, "WITH ADMIN statement is not supported"); | ||
} | ||
// roles are case insensitive in Hive | ||
String roleLowerCase = role.toLowerCase(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Specify locale, so we use the same as in other places, instead of JVMs defaults.
@@ -218,6 +219,17 @@ private CachingHiveMetastore(ExtendedHiveMetastore delegate, ExecutorService exe | |||
return loadTablePrivileges(key.getUser(), key.getDatabase(), key.getTable()); | |||
} | |||
}, executor)); | |||
|
|||
rolesCache = newCacheBuilder(expiresAfterWriteMillis, refreshMills, maximumSize) | |||
.build(asyncReloading(new CacheLoader<String, Set<String>>() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not <Void, Set<String>>
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The only valid value for Void
type is null
. But null
is not allowed to be a key in Guava cache.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You never refer to the key in cache in this case, do you?
public void createRole(String role, String grantor) | ||
throws TException | ||
{ | ||
// No-op |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not mimic e.g. createTable
's behavior - to throw the UnsupportedOperationException
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I use this mock in cache test. I don't want it to fail.
@@ -547,6 +550,34 @@ public synchronized void alterPartition(String databaseName, String tableName, P | |||
} | |||
|
|||
@Override | |||
public synchronized void createRole(String role, String grantor) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not create a synchronized object shared between roles methods instead of having synchronized method?
You don't interfere with e.g. getPartitionNames
or alterPartition
so you don't need synchronization with all synchronized methods in this class.
@@ -409,6 +413,30 @@ public synchronized void alterPartition(String databaseName, String tableName, P | |||
} | |||
|
|||
@Override | |||
public synchronized void createRole(String role, String grantor) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ditto synchronization
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You didn't address this comment.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I addressed that comment for FileBasedMetastore, but i think it doesn't matter here, because it is inmemory.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I won't insist.
@Test(groups = {HIVE_CONNECTOR, ROLES, AUTHORIZATION, PROFILE_SPECIFIC_TESTS}) | ||
public void testAccessControl() | ||
throws Exception | ||
{ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe mention here that alice
is non-admin user?
e1331c4
to
049a900
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A few more comments
} | ||
|
||
private final Type type; | ||
private final String name; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Optional<String>
instead? (for CURRENT_USER
and CURRENET_ROLE
)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've started to implement this, but it just introduces additional code with no value. This field is completely private. We never expose that null.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So maybe annotate it with @Nullable
at least?
@@ -264,6 +265,18 @@ | |||
Optional<ResolvedIndex> resolveIndex(Session session, TableHandle tableHandle, Set<ColumnHandle> indexableColumns, Set<ColumnHandle> outputColumns, TupleDomain<ColumnHandle> tupleDomain); | |||
|
|||
/** | |||
* Creates the specified role in the specified connector. | |||
* | |||
* @param grantor represent the principal specified by WITH ADMIN statement |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
represents
void createRole(Session session, String role, Optional<PrestoPrincipal> grantor, String catalog); | ||
|
||
/** | ||
* Drops the specified role in the specified connector |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
s/connector/catalog/
@@ -277,6 +277,11 @@ | |||
void dropRole(Session session, String role, String catalog); | |||
|
|||
/** | |||
* List available roles in specified connector |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
s/connector/catalog/
to be consistent with parameter name
@@ -218,6 +219,17 @@ private CachingHiveMetastore(ExtendedHiveMetastore delegate, ExecutorService exe | |||
return loadTablePrivileges(key.getUser(), key.getDatabase(), key.getTable()); | |||
} | |||
}, executor)); | |||
|
|||
rolesCache = newCacheBuilder(expiresAfterWriteMillis, refreshMills, maximumSize) | |||
.build(asyncReloading(new CacheLoader<String, Set<String>>() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You never refer to the key in cache in this case, do you?
@@ -409,6 +413,30 @@ public synchronized void alterPartition(String databaseName, String tableName, P | |||
} | |||
|
|||
@Override | |||
public synchronized void createRole(String role, String grantor) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You didn't address this comment.
@Override | ||
public void checkCanCreateRole(TransactionId transactionId, Identity identity, String role, Optional<PrestoPrincipal> grantor, String catalogName) | ||
{ | ||
requireNonNull(identity, "identity is null"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need them here? You don't store those variables. NPE will be thrown immediately a couple lines below anyway.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is done in all the methods in this class. I just wanted to keep it consistent.
049a900
to
e9ba73d
Compare
1f0ebf9
to
ff62efb
Compare
761f38f
to
c7cae56
Compare
c7cae56
to
be4482b
Compare
Depends on #6629 |
@@ -309,6 +335,10 @@ private boolean getGrantOptionForPrivilege(ConnectorTransactionHandle transactio | |||
|
|||
private boolean checkTablePermission(ConnectorTransactionHandle transaction, Identity identity, SchemaTableName tableName, HivePrivilege... requiredPrivileges) | |||
{ | |||
if (tableName.equals(ROLES) && !isAdmin(transaction, identity)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Throwing an exception is an antipattern for information schema queries -- it would be better to filter out the roles table in the filterTables() method if the user isn't admin.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, makes total sense
1ed667d
to
7e77429
Compare
The grantOption flag in PrivilegeInfo represents the WITH GRANT OPTION clause in GRANT. Also add UPDATE privilege to the SPI Privilege enum.
For SHOW ROLES, issue the query: select role_name as "Role" from catalog.information_schema.roles;
Instead of select * from information_schema.roles, SHOW CURRENT ROLES rewrites to select * from information_schema.enabled_roles. All users can see what roles they're currently using, so no need for access control checks.
Currently the only database permission we support is OWNERSHIP. Instead of creating that permission, and checking if it is granted it is more readable to just call `isDatabaseOwner` directly.
Admin user has all the available permissions for all the entities implicitly. So it may be considered as a database and table "owner" for all tables and databases. Also it has all the SELECT, INSERT, DELETE permissions implicitly.
hasGrantOptionForPrivilege cannot be used in security checks for createView because it doesn't consider the session role.
Verify that role set with `SET ROLE` is considering during the access check.
048b3a0
to
9b62dac
Compare
Blocked on #6629 |
Teradata#494